/*
 * @lc app=leetcode.cn id=355 lang=typescript
 *
 * [355] 设计推特
 */

// @lc code=start
class Twitter {
    // 存放用户信息,key = userId, value = TwitterUser
    twitterUserMap: Map<number, TwitterUser>
    constructor() {
        this.twitterUserMap = new Map<number, TwitterUser>()
    }

    postTweet(userId: number, tweetId: number): void {
        const twitterUser: TwitterUser = this.getOrCreate(userId)
        twitterUser.addTwitterData({ data: tweetId })
        this.twitterUserMap.set(userId, twitterUser)
    }

    getNewsFeed(userId: number): number[] {
        if (!this.twitterUserMap.has(userId)) return []

        const limit: number = 10
        const twitterUser = this.twitterUserMap.get(userId)
        const twitterDatas: TwitterData[] = twitterUser!.getNeedFeedByLimit(limit)

        for (const followeeId of twitterUser!.follows) {
            if (!this.twitterUserMap.has(followeeId)) continue
            twitterDatas.push(...this.twitterUserMap.get(followeeId)!.getNeedFeedByLimit(limit))
        }

        return twitterDatas.sort((a: TwitterData, b: TwitterData) => b.createAt - a.createAt).filter((value, index) => index < limit).map(twitterData => twitterData.data)
    }

    follow(followerId: number, followeeId: number): void {
        const followerTwitterUser: TwitterUser = this.getOrCreate(followerId)
        followerTwitterUser.follow(followeeId)
        this.twitterUserMap.set(followerId, followerTwitterUser)
    }

    unfollow(followerId: number, followeeId: number): void {
        const followerTwitterUser: TwitterUser = this.getOrCreate(followerId)
        followerTwitterUser.unfollow(followeeId)
        this.twitterUserMap.set(followerId, followerTwitterUser)
    }

    private getOrCreate(userId: number): TwitterUser {
        return this.twitterUserMap.get(userId) || new TwitterUser(userId)
    }
}

type TwitterData = {
    data: number,
    createAt: number,
}

class TwitterUser {
    static CREATE_AT_TIME_STAMP: number = 0;
    public userId: number
    public twitterDatas: TwitterData[]
    public follows: Set<number>

    constructor(userId: number, twitters?: TwitterData[], follows?: Iterable<number>) {
        this.userId = userId
        this.twitterDatas = twitters || []
        this.follows = new Set<number>(follows || [])
    }

    addTwitterData(twitterData: Pick<TwitterData, 'data'>): void {
        const newtwitterData: TwitterData = {
            ...twitterData,
            createAt: this.nextId(),
        }
        this.twitterDatas.push(newtwitterData)
    }

    public getNeedFeedByLimit(limit: number): TwitterData[] {
        const length: number = this.twitterDatas.length,
            start: number = length - limit >= 0 ? length - limit : 0,
            end: number = length
        // Warn: slice()函数的start参数如果是一个负数,会导致数组的元素逆序输出,这与我们的初衷相违背
        return this.twitterDatas.slice(start, end)
    }

    follow(followeeId: number): void {
        // 自己不能关注自己
        if (followeeId === this.userId) return
        this.follows.add(followeeId)
    }

    unfollow(followeeId: number): void {
        this.follows.delete(followeeId)
    }

    nextId(): number {
        return TwitterUser.CREATE_AT_TIME_STAMP++
    }
}

/**
 * Your Twitter object will be instantiated and called as such:
 * var obj = new Twitter()
 * obj.postTweet(userId,tweetId)
 * var param_2 = obj.getNewsFeed(userId)
 * obj.follow(followerId,followeeId)
 * obj.unfollow(followerId,followeeId)
 */
// @lc code=end

